package cool.ale.errorMessage.intercept;

import cool.ale.errorMessage.constant.AleErrorMessageConst;
import cool.ale.errorMessage.api.AleErrorMessageCommonResult;
import cool.ale.errorMessage.config.redis.AleErrorMessageRedisService;
import cool.ale.errorMessage.properties.AleErrorMessageBaseProperties;
import cool.ale.errorMessage.util.ErrorMsgUtil;
import cool.ale.util.constant.PlaceholderConst;
import cool.ale.util.str.FillStrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.concurrent.ConcurrentHashMap;


/**
 * ale运行时异常定义
 *
 * @author dujlc
 * @date 2022/4/19 21:22
 * @see
 * @since 1.0
 */
@Slf4j
@RestControllerAdvice
public class AleRuntimeExceptionIntercept {

    /**
     * 错误信息工具类
     */
    @Autowired
    private ErrorMsgUtil errorMsgUtil;

    /**
     * 错误信息描述属性类
     */
    @Autowired
    private AleErrorMessageBaseProperties aleErrorMessageBaseProperties;

    /**
     * redis服务接口类
     */
    @Autowired
    private AleErrorMessageRedisService aleErrorMessageRedisService;

    /**
     * 声明具体拦截器拦截什么异常类型
     * 我们这里指定拦截我们自定义的异常类，自定义的异常类继承于原生的 RuntimeException
     */
    @ExceptionHandler(value = AleRuntimeException.class)
    public AleErrorMessageCommonResult aleRuntimeExceptionIntercept(AleRuntimeException e){
        AleErrorMessageCommonResult aleErrorMessageCommonResult = errorStatusConvertMessage(e.getStatus(), e.getObjects());
        return aleErrorMessageCommonResult;
    }

    /**
     * 将业务代码中传递的 status 拿出来转换成国际化文本中相应的 message，
     * 并将status和message一块封装业务异常模型中并返回
     * @param status    业务端传入的状态码
     * @param objects   业务端传入的占位符参数集合
     * @return          返回一个业务异常模型
     */
    private AleErrorMessageCommonResult errorStatusConvertMessage(String status, Object[] objects){
        // 从redis中获取数据
        String originalMsg = getErrorMsgByRedis(status);
        if (null == originalMsg){
            // 1、重新加载国际化文本，获取原始值
            ConcurrentHashMap<String, String> errorMap = loadEffectLanguageAndGetErrorMap();
            if (null == errorMap){
                return AleErrorMessageCommonResult.failed(status, status);
            }
            originalMsg = errorMap.get(status);
            // 2、将数据缓存进redis
            errorMsgUtil.writeErrorMapToRedis(errorMap);
        }
        // 3、拼接返回信息
        if (null == originalMsg){
            return AleErrorMessageCommonResult.failed(status, status);
        }
        String finalMsg = matchPlaceholderParameter(originalMsg, objects);
        // 4、返回最终拼接的信息
        return AleErrorMessageCommonResult.failed(status, finalMsg);
    }

    /**
     * 获取数据从redis中
     * @param status        代码中的业务异常码
     * @return      返回redis中查到的业务异常信息
     */
    private String getErrorMsgByRedis(String status){
        // 1、组装真正的key
        String errorMessagePrefixNameOne = "";
        // 获取时取出错误信息前缀
        String getErrorMessagePrefixOne = aleErrorMessageBaseProperties.getErrorMessagePrefixNameOne();
        if (null != getErrorMessagePrefixOne){
            errorMessagePrefixNameOne = getErrorMessagePrefixOne;
        }
        String redisStatus = errorMessagePrefixNameOne + AleErrorMessageConst.ERROR_MSG_PREFIX_TWO + status;
        // 2、从redis中获取原始数据并返回
        return aleErrorMessageRedisService.get(redisStatus);
    }

    /**
     * 加载当前生效的国际化文本生效语言以及返回相应文件的业务异常map集合
     * @return      返回全部的信息的map
     */
    private ConcurrentHashMap<String, String> loadEffectLanguageAndGetErrorMap(){
        return errorMsgUtil.loadEffectLanguageAndGetErrorMap();
    }

    /**
     * 调用中括号占位符工具类将参数集合填充进去
     * @param message       要填充的源字符串
     * @param objects       要填充的参数集合
     * @return       返回填充好的源字符串
     */
    private String matchPlaceholderParameter(String message, Object[] objects){
        return FillStrUtil.fillStrByAppointPlaceholder(
                message,
                PlaceholderConst.MIDDLE_BRACKET_LEFT,
                PlaceholderConst.MIDDLE_BRACKET_RIGHT,
                PlaceholderConst.MIDDLE_BRACKET_PATTERN,
                objects);
    }
}

